#include "list.h"
#include <stdio.h>

/**
 * @brief Create a list from heap
 * @retval list_t* list pointer
 */
list_t *listCreate(void)
{
    list_t *plist = _listMalloc(sizeof(list_t));
    if (plist == NULL)
        return NULL;
    plist->head = NULL;
    plist->len = 0;
    return plist;
}

/**
 * @brief Create a node from heap
 * @param data push data to node
 * @retval node_t* new node pointer
 */
node_t *listNewNode(void *data)
{
    node_t *pnode;
    pnode = _listMalloc(sizeof(node_t));
    if (pnode == NULL)
        return 0;
    pnode->data = data;
    pnode->next = NULL;
    pnode->prior = NULL;
    return pnode;
}

/**
 * @brief Go next node
 * @param node current node
 * @retval node_t* 
 */
_LIST_USE_INLINE node_t *listNext(node_t **node)
{
    _listAssert(node != NULL);
    return ((*node)->next) ? (*node = (*node)->next) : NULL;
}

/**
 * @brief Go last node
 * @param node current node
 * @retval node_t* 
 */
_LIST_USE_INLINE node_t *listBack(node_t** node)
{
    _listAssert(node != NULL);
    return ((*node)->prior) ? (*node = (*node)->prior) : NULL;
}

/**
 * @brief Get pos node
 * @param root Root node
 * @param index  Node index [0,listLength-1]
 * @retval Position node list_t*
 */
node_t *listGoNode(list_t *root, uint32_t index)
{
    _listAssert(root != NULL);
    _listAssert(index < root->len);

    node_t *pnode = root->head;
    while (index--)
    {
        _listAssert(pnode != NULL);
        pnode = pnode->next;
    }
    return pnode;
}

/**
 * @brief To list tail node
 * @param root Root node
 * @retval node_t* tail Node
 */
node_t *listGoTail(list_t *root)
{
    _listAssert(root != NULL);

    if (root->head == NULL)
        return NULL;
    node_t *pnode = root->head;
    while (pnode->next)
    {
        pnode = pnode->next;
    }
    return pnode;
}

/**
 * @brief Search data from list
 * @param root
 * @param data
 * @retval node_t*
 */
node_t *listDataSearch(list_t *root, void *data)
{
    _listAssert(root != NULL);

    node_t *pnode = root->head;
    while (pnode)
    {
        if (pnode->data == data)
            return pnode;
        pnode = pnode->next;
    }
    return NULL;
}


_LIST_USE_INLINE void listWrite(list_t *root, uint32_t index, void *data)
{
    _listAssert(root != NULL);
    _listAssert(index < root->len);

    listGoNode(root, index)->data = data;
}

_LIST_USE_INLINE void *listRead(list_t *root, uint32_t index)
{
    _listAssert(root != NULL);
    _listAssert(index < root->len);

    return listGoNode(root, index)->data;
}

/**
 * @brief Insert node at pos next
 * @param root Root node
 * @param node Need to insert node
 * @param pos  Insert Position [0,listLength]
 * @retval int true:listLength / false:0
 */
int listInsertNodeAt(list_t *root, node_t *node, uint32_t pos)
{
    _listAssert(root != NULL);
    _listAssert(node != NULL);
    _listAssert(pos < root->len+1);

    node_t *pnode;
    if(pos == 0)
    {
        if(root->head != NULL)
        {
            node->next = root->head;
            node->next->prior = node;
        }
        root->head = node;
        return ++root->len;
    }
    pnode = listGoNode(root, pos-1);
    if (pnode->next == NULL) // The last of node
    {
        pnode->next = node;
        node->prior = pnode;
    }
    else
    {
        node->next = pnode->next;
        node->next->prior = node;
        node->prior = pnode;
        pnode->next = node;
    }
    return ++root->len;
}

/**
 * @brief Add new node to list tail
 * @param root
 * @param data
 * @retval int true:listLength / false:0
 */
int listPusht(list_t *root, void *data)
{
    _listAssert(root != NULL);

    node_t *pnode;
    pnode = _listMalloc(sizeof(node_t));
    if (pnode == NULL)
        return 0;
    pnode->data = data;
    if (root->head != NULL)
    {
        pnode->prior = listGoTail(root);
        pnode->prior->next = pnode;
    }
    else
    {
        root->head = pnode;
        pnode->prior = NULL;
    }
    pnode->next = NULL;
    return ++root->len;
}

/**
 * @brief Add new node to list head
 * @param root
 * @param data
 * @retval int true:listLength / false:0
 */
int listPushh(list_t *root, void *data)
{
    _listAssert(root != NULL);

    node_t *pnode;
    pnode = _listMalloc(sizeof(node_t));
    if (pnode == NULL)
        return 0;
    pnode->data = data;
    if (root->head != NULL)
    {
        pnode->next = root->head;
        pnode->next->prior = pnode;
        root->head = pnode;
    }
    else
    {
        root->head = pnode;
        pnode->next = NULL;
    }
    pnode->prior = NULL;
    return ++root->len;
}

/**
 * @brief list add node to pos next
 * @param root
 * @param data
 * @param pos [0,listLenth]
 * @retval int true:listLength / false:0
 */
int listAddNodeAt(list_t *root, void *data, uint32_t pos)
{
    _listAssert(root != NULL);

    node_t *pnode;
    pnode = _listMalloc(sizeof(node_t));
    if (pnode == NULL)
        return 0;
    pnode->data = data;
    pnode->next = NULL;
    pnode->prior = NULL;
    return listInsertNodeAt(root, pnode, pos);
}

_LIST_STATIC_INLINE int listDeleteNode(list_t *root, node_t *pnode)
{
    _listAssert(root != NULL);
    _listAssert(pnode != NULL);

    if (pnode->prior == NULL)
        root->head = pnode->next; // First Node
    else
        pnode->prior->next = pnode->next;
    if (pnode->next != NULL)
        pnode->next->prior = pnode->prior;
    _listFree(pnode);
    return --root->len;    
}


/**
 * @brief Delete node from pos
 * @param root Root node
 * @param pos node Position [0,listLength-1]
 * @retval int true:listLength / false:0
 */
int listDeleteNodeAt(list_t *root, uint32_t pos)
{
    _listAssert(root != NULL);

    node_t *pnode = listGoNode(root, pos);
    return listDeleteNode(root, pnode);
}

/**
 * @brief Recycle memory from list
 * @param root
 */
void listDestroy(list_t **root)
{
    _listAssert(root != NULL);

    uint32_t i = (*root)->len;
    node_t *pnode;
    while (i--)
    {
        pnode = (*root)->head;
        (*root)->head = pnode->next;
        _listFree(pnode);
    }
    _listFree(*root);
    *root = NULL;
}

/**
 * @brief popping data from the tail list node
 * @param root 
 * @retval void* data or NULL
 */
void *listPopt(list_t *root)
{
    _listAssert(root != NULL);

    if (root->len == 0)
        return NULL;
    void *pdata = NULL;
    node_t *pnode = listGoTail(root);
    pdata = pnode->data;
    listDeleteNode(root, pnode);
    return pdata;
}

/**
 * @brief popping data from the head list node
 * @param root 
 * @retval void* data or NULL
 */
void *listPoph(list_t *root)
{
    _listAssert(root != NULL);

    if (root->len == 0)
        return NULL;
    void *pdata = NULL;
    node_t *pnode = listGoNode(root, 0);
    pdata = pnode->data;
    listDeleteNode(root, pnode);
    return pdata;
}

/**
 * @brief Convert Array to List
 * @param root list root
 * @param arr array point
 * @param len arr length
 * @retval int 
 */
int listArrayToList(list_t *root, void *arr, uint32_t sizeofType, uint32_t len)
{
    _listAssert(root != NULL);
    uint32_t i;
    uint8_t* p = (uint8_t*)arr;
    for (i = 0; i < len; i++)
    {
        listPusht(root, &p[i * sizeofType]);
    }
    return i;
}

/**
 * @brief Free memory from data
 * @param node 
 * @param data_free 
 */
_LIST_USE_INLINE void listDataFree(node_t *node, void data_free(void *))
{
    _listAssert(node != NULL);
    _listAssert(data_free != NULL);
    data_free(node->data);
    node->data = NULL;
}

/**
 * @brief Free all data memory
 * @param root 
 * @param data_free 
 */
void listDatasFree(list_t *root, void data_free(void *))
{
    _listAssert(root != NULL);
    _listAssert(data_free != NULL);

    uint32_t i = root->len;
    node_t *pnode = root->head;
    while (i--)
    {
        data_free(pnode->data);
        pnode->data = NULL;
        pnode = pnode->next;
    }
}

